package cn.wp.common.core.plugin.scheduler;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.wp.common.core.WpProperties;
import cn.wp.common.core.WpVar;
import cn.wp.common.core.exception.WpException;
import cn.wp.common.core.plugin.email.MailService;
import lombok.extern.slf4j.Slf4j;
import org.assertj.core.util.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronSequenceGenerator;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring5.SpringTemplateEngine;

import java.io.InputStream;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;

/**
 * 定时器服务类
 *
 * 参考地址：blog.csdn.net/weixin_37591536/article/details/82150933
 *
 * Created by chenyb on 2019/10/23
 */
@Service
@Slf4j
public class SchedulerService {

    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;

    @Autowired
    private SchedulerContainer schedulerContainer;

    @Autowired
    private SchedulerProperties schedulerProperties;

    @Autowired
    private SpringTemplateEngine springTemplateEngine;

    @Autowired
    private MailService mailService;

    @Autowired
    private WpProperties wpProperties;

    /*// 查询单个任务
    public SchedulerTask getTask(Map<String,Object> parameters){
        SchedulerDao schedulerDao = ApplicationContextUtil.getBean(SchedulerDao.class);
        return schedulerDao.selectTask(parameters);
    };

    // 查询所有任务
    public List<SchedulerTask> getTaskList(Map<String,Object> parameters){
        SchedulerDao schedulerDao = ApplicationContextUtil.getBean(SchedulerDao.class);
        return schedulerDao.selectTaskList(parameters);
    };*/

    // 添加任务到任务池
    /*public void addTask(SchedulerTask schedulerTask){
        schedulerContainer.addTask(schedulerTask.getTaskId(),schedulerTask);
    }*/

    // 启动新任务
    public String startTask(SchedulerTask schedulerTask,boolean isTest){
        // 未启用定时器
        if(!schedulerProperties.isEnabled() && !isTest){
            log.warn(schedulerTask + " Not running because the timer is disabled in the configuration file");
            return schedulerTask.getTaskId();
        }

        // 任务已存在
        SchedulerTask storeTask = schedulerContainer.getTask(schedulerTask.getTaskId());
        if(storeTask != null && storeTask.getStatus() > 0){
            log.warn(schedulerTask + " The task already exists. Do not open a new task");
            return schedulerTask.getTaskId();
        }

        // 启动任务实例
        ScheduledFuture future = threadPoolTaskScheduler.schedule(schedulerTask.getRunnable(),new Trigger(){
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext){
                // 计算最近3次的运行时间
                CronSequenceGenerator cronSequenceGenerator = new CronSequenceGenerator(schedulerTask.getCron());
                List<String> nextRunTimeList = CollectionUtil.newArrayList();
                Date lastTime = new Date();
                int len = 3;
                for(int i = 0 ;i < len;i++){
                    lastTime = cronSequenceGenerator.next(lastTime); //下次执行时间
                    nextRunTimeList.add(DateUtil.formatDateTime(lastTime));
                    schedulerTask.setNextRunTimeList(nextRunTimeList);
                }

                if(schedulerTask.isInit()){
                    // 设置为非初始化状态
                    schedulerTask.setInit(false);
                    return new CronTrigger(schedulerTask.getCron()).nextExecutionTime(triggerContext);
                }

                // 运行完成方法调用
                schedulerTask.getRunnable().runFinish(schedulerTask);
                String cron = schedulerTask.getCron();
                schedulerContainer.updateTask(schedulerTask.getTaskId(),schedulerTask);
                log.info("定时器信息更新：" + schedulerTask);
                return new CronTrigger(cron).nextExecutionTime(triggerContext);
            }
        });

        // 调度实例设置
        schedulerTask.setFuture(future);
        // 设置为等待触发运行状态
        schedulerTask.setStatus(2);
        schedulerContainer.addTask(schedulerTask.getTaskId(),schedulerTask);
        log.info("启动任务实例：" + schedulerTask);
        return schedulerTask.getTaskId();
    };

    // 启动新任务
    public String startTask(SchedulerTask schedulerTask){
        return startTask(schedulerTask,false);
    };

    // 根据ID启动任务
    public SchedulerTask startTask(String taskId) {
        SchedulerTask schedulerTask = schedulerContainer.getTask(taskId);
        if(schedulerTask == null){
            throw new WpException("认为启动失败，调度池根据任务ID[ " + taskId + " ]未找到可执行的任务");
        }

        // 先停止
        this.stopTask(taskId);
        // 启动任务
        this.startTask(schedulerTask);
        return schedulerTask;
    }

    // 获取任务信息
    public SchedulerTask getTaskById(String taskId) {
        return schedulerContainer.getTask(taskId);
    }

    // 启动任务列表
    public List<String> startTasks(List<SchedulerTask> schedulerTasks){
        List<String> taskIds = CollectionUtil.newArrayList();
        for(SchedulerTask task : schedulerTasks){
            this.startTask(task);
            taskIds.add(task.getTaskId());
        }
        return taskIds;
    };

    // 查询正在运行的所有任务
    public List<SchedulerTask> getRunnerTaskList(){
        List<SchedulerTask> schedulerTasks = CollectionUtil.newArrayList();
        Map<String,SchedulerTask> taskMap = schedulerContainer.getTaskMap();
        for(Map.Entry<String,SchedulerTask> entry : taskMap.entrySet()){
            SchedulerTask schedulerTask = entry.getValue();
            if(schedulerTask.getStatus() == 1){
                schedulerTasks.add(schedulerTask);
            }
        }
        return schedulerTasks;
    };

    // 查询所有任务
    public List<SchedulerTask> getTaskList(){
        List<SchedulerTask> schedulerTasks = CollectionUtil.newArrayList();
        Map<String,SchedulerTask> taskMap = schedulerContainer.getTaskMap();
        for(Map.Entry<String,SchedulerTask> entry : taskMap.entrySet()){
            SchedulerTask schedulerTask = entry.getValue();
            schedulerTasks.add(schedulerTask);
        }
        return schedulerTasks;
    };

    // 判断任务是否正在运行
    public boolean isRunning(String taskId){
        SchedulerTask schedulerTask = schedulerContainer.getTask(taskId);
        return schedulerTask.getStatus() == 1;
    };

    // 终止任务
    public SchedulerTask stopTask(String taskId){
        SchedulerTask schedulerTask = schedulerContainer.getTask(taskId);
        // 更新状态-未运行状态
        schedulerTask.setStatus(0);
        // 设置为初始状态
        schedulerTask.setInit(true);
        schedulerContainer.updateTask(taskId,schedulerTask);
        if(schedulerTask != null){
            // 停止任务
            schedulerTask.getFuture().cancel(true);
            log.info("终止任务实例：" + schedulerTask);
        }else{
            log.warn("调度池根据任务ID[ " + taskId + " ]未找到可执行的任务，任务停止失败");
        }

        return schedulerTask;
    };

    // 删除任务
    public SchedulerTask removeTask(String taskId){
        this.stopTask(taskId);
        return schedulerContainer.removeTask(taskId);
    };

    // 刷新任务
    public void refreshTask(SchedulerTask schedulerTask){
        schedulerContainer.updateTask(schedulerTask.getTaskId(),schedulerTask);
    };

    /**
     * 定时任务执行统计报表
     * @param title 标题
     * @param time 每日几点发送
     * @return
     */
    public String report(String title,String time){
        String sysName = StrUtil.isBlank(title) ? "" : title;
        String timeStr = StrUtil.isBlank(time) ? DateUtil.formatTime(new Date()) : time;
        // 2019-11-18 08:00:00 2019-11-19 08:00:00
        // 2019-11-19 08:00:00 2019-11-20 08:00:00
        // 统计周期前一天 08:00 至邮件发送当天 08:00
        Map<String,SchedulerTask> taskMap = schedulerContainer.getTaskMap();
        int total = taskMap.size(), runTotal = 0, unRunTotal = 0,runSuccessTotal = 0,runFailTotal = 0,runningTotal = 0;
        List<SchedulerTask> runTaskList = Lists.newArrayList();
        List<SchedulerTask> runningTaskList = Lists.newArrayList();
        List<SchedulerTask> failRunTaskList = Lists.newArrayList();
        List<SchedulerTask> unRunTaskList = Lists.newArrayList();

        Date endDate = DateUtil.parseDateTime(DateUtil.formatDate(new Date()) + " " + timeStr);
        Date beginDate = DateUtil.offsetDay(endDate, -1);
        for(Map.Entry<String,SchedulerTask> taskEntry: taskMap.entrySet()){
            SchedulerTask task = taskEntry.getValue();
            //String lastEndRuntime = task.getLastEndRunDatetime();
            String lastStartRuntime = task.getLastStartRunDatetime();

            // 未开始运行的情况
            if(StrUtil.isBlank(lastStartRuntime)){
                if(task.getStatus() == 1){
                    runTotal++;
                    runningTotal++;
                    runningTaskList.add(task);
                }else if ((task.getStatus() == 2 || task.getStatus() == 0) && task.getLastRunSuccess() == 0){
                    unRunTotal++;
                    unRunTaskList.add(task);
                }else if(task.getLastRunSuccess() == 1){
                    runTotal++;
                    runFailTotal++;
                    failRunTaskList.add(task);
                }
                continue;
            }

            Date lastRunDatetime = DateUtil.parseDateTime(lastStartRuntime);
            if(DateUtil.compare(endDate,lastRunDatetime) == 1 && DateUtil.compare(lastRunDatetime,beginDate) >=0){
                runTotal++;
                if(task.getLastRunSuccess() == 0){
                    runTaskList.add(task);
                    runSuccessTotal++;
                }else{
                    runFailTotal++;
                    failRunTaskList.add(task);
                }
            }else if(task.getStatus() == 1){
                runTotal++;
                runningTotal++;
                runningTaskList.add(task);
            }else{
                unRunTotal++;
                unRunTaskList.add(task);
            }
        }

        // 错误邮件模板
        InputStream is = this.getClass().getResourceAsStream("/templates/email-tpl/scheduler-run-report.tpl");
        String htmlString = IoUtil.read(is, WpVar.ENCODE);
        IoUtil.close(is);

        Context context = new Context();
        context.setVariable("sysName",sysName);
        context.setVariable("createDatetime",DateUtil.now());
        context.setVariable("startDatetime",DateUtil.formatDateTime(beginDate));
        context.setVariable("endDatetime",DateUtil.formatDateTime(endDate));
        context.setVariable("total",total);
        context.setVariable("runTotal",runTotal);
        context.setVariable("runSuccessTotal",runSuccessTotal);
        context.setVariable("runFailTotal",runFailTotal);
        context.setVariable("runningTotal",runningTotal);
        context.setVariable("unRunTotal",unRunTotal);
        context.setVariable("runTaskList",runTaskList);
        context.setVariable("failRunTaskList",failRunTaskList);
        context.setVariable("runningTaskList",runningTaskList);
        context.setVariable("unRunTaskList",unRunTaskList);
        String report = springTemplateEngine.process(htmlString,context);
        return report;
    };

    // 定时任务统计报表发送邮件
    public void sendReportEmail(String title,String time){
        String body = report(title,time);
        mailService.sendMime(wpProperties.getEmailDevops(),"定时器运行统计报表（"+ DateUtil.date().toDateStr() +"）",body);
    };

}
